/**
 * Title :	jFamilyTree generator
 * Description: jFamilyTree generator
 * Version:	provider
 * Copyright (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.generator.tree;

import java.awt.Color;

import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import jFamilyTree.generator.Controller;

/**
 * Tree
 * 
 * @author Bernard Bou
 */
public class Tree extends JTree implements TreeSelectionListener // , DragGestureListener, DragSourceListener, DropTargetListener
{
	static private final long serialVersionUID = 1L;

	/**
	 * Whether newly-created node receives focus
	 */
	static public boolean focusParent = true;

	/**
	 * Whether data have been changed
	 */
	public boolean isDirty;

	/**
	 * Controller (reference)
	 */
	private Controller theController;

	// C O N S T R U C T

	/**
	 * Constructor
	 */
	public Tree()
	{
		super((TreeModel) null);
		isDirty = false;

		setBackground(Color.WHITE);
		setShowsRootHandles(true);
		setToggleClickCount(2);
		setScrollsOnExpand(true);
		setEditable(false);
		setFont(new java.awt.Font("Dialog", 0, 10)); //$NON-NLS-1$
		setToolTipText("Tree"); //$NON-NLS-1$
		putClientProperty("JTree.lineStyle", "Horizontal"); //$NON-NLS-1$ //$NON-NLS-2$

		// renderer
		setCellRenderer(new Renderer());

		// selection handler
		addTreeSelectionListener(this);

		// drag and drop
		getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		setDragEnabled(true);
		setTransferHandler(new TreeTransferHandler());
	}

	/**
	 * Connect to controller
	 * 
	 * @param thisController
	 *        controller
	 */
	public void connect(Controller thisController)
	{
		theController = thisController;
	}

	// E X P A N D

	/**
	 * Expand tree
	 */
	public void expandAll()
	{
		Object thisRoot = getModel().getRoot();
		if (thisRoot == null)
			return;
		TreePath thisPath = new TreePath(thisRoot);
		expandAll(thisPath);
	}

	/**
	 * Collapse tree
	 */
	public void collapseAll()
	{
		TreeModel thisModel = getModel();
		TreeNode thisRoot = (TreeNode) thisModel.getRoot();
		if (thisRoot == null)
			return;
		DefaultMutableTreeNode[] theseNodes = { //
		(DefaultMutableTreeNode) thisRoot.getChildAt(0).getChildAt(0).getChildAt(0), // root node
				(DefaultMutableTreeNode) thisRoot.getChildAt(0).getChildAt(1), // edges
				(DefaultMutableTreeNode) thisRoot.getChildAt(1) // tools
		};
		for (DefaultMutableTreeNode thisNode : theseNodes)
		{
			TreePath thisPath = new TreePath(thisNode.getPath());
			collapseAll(thisPath);
		}
	}

	/**
	 * Expand tree
	 * 
	 * @param thisPath
	 *        tree path to expand
	 */
	public void expandAll(TreePath thisPath)
	{
		// this
		expandPath(thisPath);

		// recursive call for each child
		Object thisNode = thisPath.getLastPathComponent();
		int thisCount = getModel().getChildCount(thisNode);
		for (int i = 0; i < thisCount; i++)
			expandAll(thisPath.pathByAddingChild(getModel().getChild(thisNode, i)));
	}

	/**
	 * Collapse tree
	 * 
	 * @param thisPath
	 *        tree path to collapse
	 */
	public void collapseAll(TreePath thisPath)
	{
		// recursive call for each child
		Object thisNode = thisPath.getLastPathComponent();
		int thisCount = getModel().getChildCount(thisNode);
		for (int i = 0; i < thisCount; i++)
			collapseAll(thisPath.pathByAddingChild(getModel().getChild(thisNode, i)));

		// this
		collapsePath(thisPath);
	}

	// M O D I F I C A T I O N

	/**
	 * Add node to parent
	 * 
	 * @param thisParentNode
	 *        node
	 * @param thisNode
	 *        node
	 */
	public void addToParent(DefaultMutableTreeNode thisParentNode, DefaultMutableTreeNode thisNode)
	{
		// model
		DefaultTreeModel thisModel = (DefaultTreeModel) getModel();

		// tree insert (model insert for events to be fired)
		thisModel.insertNodeInto(thisNode, thisParentNode, thisModel.getChildCount(thisParentNode));

		// select
		TreePath thisPath = new TreePath(thisModel.getPathToRoot(thisNode));
		makeVisible(thisPath);
		scrollPathToVisible(thisPath);
		if (!focusParent)
			addSelectionPath(thisPath);
		isDirty = true;
	}

	/**
	 * Remove from parent
	 * 
	 * @param thisNode
	 *        node
	 */
	public void removeFromParent(DefaultMutableTreeNode thisNode)
	{
		// model
		DefaultTreeModel thisModel = (DefaultTreeModel) getModel();

		// point to parent
		javax.swing.tree.TreeNode thisParent = thisNode.getParent();
		int thisIndex = -1;
		if (thisParent != null)
			thisIndex = thisParent.getIndex(thisNode);

		// tree remove (model remove for events to be fired)
		thisModel.removeNodeFromParent(thisNode);

		// select
		if (thisParent != null)
		{
			thisIndex = Math.min(thisIndex, thisParent.getChildCount() - 1);
			javax.swing.tree.TreeNode thisSelection = (!focusParent && thisIndex > -1) ? thisParent.getChildAt(thisIndex) : thisParent;
			addSelectionPath(new TreePath(thisModel.getPathToRoot(thisSelection)));
		}
		isDirty = true;
	}

	// S E L E C T I O N . H A N D L I N G

	/**
	 * Select node
	 * 
	 * @param thisNode
	 *        node
	 */
	public void select(DefaultMutableTreeNode thisNode)
	{
		theController.picked(thisNode.getUserObject());
	}

	// T R E E . S E L E C T I O N . L I S T E N E R

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent)
	 */
	public void valueChanged(TreeSelectionEvent thisE)
	{
		// return if null selection
		TreePath thisPath = getSelectionPath();
		if (thisPath == null)
			return;

		// get new selected element
		Object thisObject = thisPath.getLastPathComponent();
		DefaultMutableTreeNode thisNode = (DefaultMutableTreeNode) thisObject;
		select(thisNode);
	}
}
